using System;
using System.Collections.Generic;
using System.Text;

using System.IO;

namespace Athena
{
    public class HistoryManager
    {

        class HistoryItem
        {
            string _sLabel;
            byte[] _oModel;
            /// <summary>
            /// Initializes a new instance of the HistoryItem class.
            /// </summary>
            /// <param name="sLabel"></param>
            /// <param name="oModel"></param>
            public HistoryItem(string sLabel, Model oModel)
            {
                _sLabel = sLabel;

                MemoryStream oStream = new MemoryStream();
                oModel.SaveModel(oStream);
                oStream.Close();
                _oModel = oStream.ToArray();
                
            }

            public string Label
            {
                get { return _sLabel; } 
            }

            public Model Model
            {
                get
                {
                    MemoryStream oStream = new MemoryStream(_oModel);                                        
                    Program.Model.OpenModel(oStream);                    
                    return Program.Model;
                }
            }
        }

        private static HistoryManager _oHistory = new HistoryManager();
        
        private HistoryManager()
        {
        }

        /// <summary>
        /// Keeps track of the changes in the program by storing the 
        /// previous state of the program. Use MAX to set how many changes
        /// to store
        /// </summary>
        public static HistoryManager History
        {
            get
            {
            	return _oHistory ;
            }
        }

        static int _MAX = 100;

        /// <summary>
        /// Set the maximum number of history to store.
        /// Setting this value to a smaller number can save memory and speed up the program
        /// </summary>
        public static int MAX
        {
            get { return _MAX; }
            set
            {
                _MAX = value;
                while (_oHistory._oUndoList.Count > _MAX)
                {
                    _oHistory._oUndoList.RemoveLast();
                }
                while (_oHistory._oRedoList.Count > _MAX)
                {
                    _oHistory._oRedoList.RemoveLast();
                }
            }
        }

        LinkedList<HistoryItem> _oUndoList = new LinkedList<HistoryItem>();
        LinkedList<HistoryItem> _oRedoList = new LinkedList<HistoryItem>();

        public void Clear()
        {
            _oUndoList.Clear();
            _oRedoList.Clear();
        }

        void SetControllPoint(string sLabel, HistoryItem oItem)
        {
            if (_oRedoList.Count > 0) _oRedoList.Clear();
            _oUndoList.AddFirst(oItem);
            if (_oUndoList.Count > MAX) _oUndoList.RemoveLast();
            
        }
        public void SetControllPoint(string sLabel)
        {
            SetControllPoint(sLabel, CreateHistoryItem(sLabel));
        }

        static HistoryItem CreateHistoryItem(string sLabel)
        {
            Utility.SetMessage(sLabel);
            return new HistoryItem(sLabel, Program.Model);
        }

        public void Undo()
        {
            if (_oUndoList.Count == 0) return;
            HistoryItem oItem = _oUndoList.First.Value;
            _oUndoList.RemoveFirst();

            HistoryItem oUndoItem = CreateHistoryItem("Undo: " + oItem.Label);
            Program.Model = oItem.Model;

            _oRedoList.AddFirst(oUndoItem);
            if (_oRedoList.Count > MAX) _oRedoList.RemoveLast();
        }

        HistoryItem _oTempItem = null;
        public void BeginRecord(string sLabel)
        {
            _oTempItem = CreateHistoryItem(sLabel);
        }

        public void ClearRecord()
        {
            _oTempItem = null;
        }

        public void EndRecord()
        {
            if (_oTempItem != null)
            {
                SetControllPoint(_oTempItem.Label, _oTempItem);
                _oTempItem = null;
            }
        }


        public void Undo(int nSteps)
        {
            for (int i = 0; i < nSteps; i++)
            {
                Undo();
            }
        }

        public void Redo()
        {
            if (_oRedoList.Count == 0) return;
            HistoryItem oItem = _oRedoList.First.Value;
            _oRedoList.RemoveFirst();
            HistoryItem oRedoItem = CreateHistoryItem("Redo: " + oItem.Label);
            Program.Model = oItem.Model;

            _oUndoList.AddFirst(oRedoItem);
            if (_oUndoList.Count > MAX) _oUndoList.RemoveLast();
        }

        public void Redo(int nSteps)
        {
            for (int i = 0; i < nSteps; i++)
            {
                Redo();
            }
        }

    }
}
